﻿#ifndef _REC_WRITE_BASE_
#define _REC_WRITE_BASE_

#include "fifo_buf.h"
#include "rec_info.h"
#include "frame_utility.h"
#include "rec_frmidx.h"
#include "serial_file_base.h"

#include <vector>
using namespace std;

class LHN_API CRecWriteBase
{
public:
	CRecWriteBase();
	~CRecWriteBase();
public:
	int Init(bool bUserFIFO,int fifoSize);
	int InitFifoBuf(int bufSize = STFS_REC_IDX_SIZE);

	int OpenFile(const char * storagePath, uint32_t fileIndex, uint32_t fileSize);
	virtual int Open(const char * dev, uint32_t fileIndex, uint32_t fileSize,int64_t startTime = 0,PSTFS_RECINFO_EXTERN pExternInfo = NULL) = 0;
//	virtual int OpenRepairFile(REC_FILE_INFO & recInfo,uint32_t fileLen,int64_t startTime = 0) = 0; //追加写入修复的文件,节省空间.
	virtual int WriteFrame(uchar_t * frameBuf ,uint32_t dataSize) = 0;
	virtual int RepairFile(char*  diskName,uint32_t fileIndex,uint64_t fileLBA,uint32_t recSize,int64_t startTime,uint32_t& fileSize,int64_t & endTime) = 0;
	virtual int Close();
    virtual uint32_t GetWritePercent() = 0;
	int IsNormalClose();
	int SeekToTime(time_t seekTm);
	int ReadFileInfo();
	int ReadIndexInfo(uchar_t * buf,int buf_size);
	int ReadGPSInfo(uchar_t * buf,int buf_size);
	int WriteGPSInfo(uchar_t * buf ,int size);

    //暂停录像,产生分段
    void PauseRec(REC_FILE_INFO & recordInfo);
    //恢复录像
    void Resume(REC_FILE_INFO & recordInfo);

	//录像刷新数据线程使用
	int  Flush_HeadInfo(bool bEndRecord);
	void Start_FlushData(uint32_t dataOffset);
	int  Flush_FIFOData(int time_out, int cache_water);
	int GetSegmentEndTime(int idx,int64_t startTime,int64_t & endTime);

	void SetStorageName(char * pDevName)
	{
		m_storageName = pDevName;
	}
	char * GetRecFileName()
	{
		return m_pFile->get_device_name();
	}
protected:
	//建立索引
	//刷新缓冲数据
	void InitHeadInfo(uint32_t fileSize,int fileIndex,int64_t startTime,PSTFS_RECINFO_EXTERN pExternInfo);
	void SetSegmentInfo(STFS_REC_SEG & curSeg);
	int  WriteFIFO(uchar_t * data_buf, uint32_t data_size);
	int	 AnsureKeyFrameSpace(uint32_t reserveSize = 0);
	void GetDataSize2IFrm(uint32_t & dataSize,int lastIdxCnt);
	int  CreateRecIndex(int frmType,int frmLen,int64_t frmTm,uint32_t curPos);
	int  WriteRecHeadInfo();
	bool IsTimeOut(int timeOutMS);
	int WriteDataAlign(const void * dataBuf , int dataSize);
	int DetectFileHead(uchar_t * dataBuf, int dataSize);
	int ClearIndexArea();
	bool IsSwitchFile(int curOffset);
public:
	bool IsKeyFrame(int frmType)
	{
		if(frmType != IFrame &&
		  frmType != MFrame)
		{
			return false;
		}
		return true;
	}

	bool FlushDataOK(uint32_t cache_water)
	{
		if(m_pfifo_buf == NULL )
		{
			return false;
		}
		if(m_bstop || (m_pfifo_buf->GetCurWarter() >= cache_water))
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	uint32_t GetCurPos()
	{
		return m_curPos;
	}
	int64_t GetCurSegStartTime()
	{
		return m_curSeg.tmStart;
	}

	uint32_t GetCurSegNO()
	{
		return m_curSeg.segNO;
	}

	bool IsOverMaxSeg()
	{
		if(m_curSeg.segNO >= STFS_MAX_SEG_NUM )
		{
			return true;
		}
		else
		{
			return false;
		}
	}

    uint32_t GetLastOffset()
    {
		if( NULL == m_pFile)
		{
			return 0;
		}
        return m_pFile->get_cur_offset();
    }

    uint32_t GetFreeSize()
    {
        return m_fileHead.fileSpace - m_curPos;
    }

    uint32_t GetCurSegSize()
    {
        return m_curPos - m_curSeg.data_start;
    }

    bool IsOpened()
    {
		if( NULL == m_pFile)
		{
			return false;
		}
        return !m_pFile->is_closed();
    }



    int64_t GetLastFrmTime()
    {
        return  m_fileHead.endTime;
    }

	int GetRecTimes()
	{
		return  (int)((m_fileHead.endTime- m_curSeg.tmStart)/1000);
	}

	int GetIndexNO()
	{
        return  (int)m_recIdx.NO;
	}
	uint32_t GetRecIdxNo()
	{
		return m_fileHead.indexNO;
	}
	//获取该段录像的时间长度
	int GetCurSegTimeS()
	{
		return (int)((m_fileHead.endTime - m_curSeg.tmStart)/1000);
	}

	STFS_REC_FILEHEAD * GetFileHead()
	{
		return &m_fileHead;
	}

	CSerialFileBase *	GetRecFile()
	{
		return m_pFile;
	}

	int GetLastErr()
	{
		if( NULL == m_pFile)
		{
			return -1;
		}
		return m_pFile->GetLastErr();
	}

 	int get_channel()
	{
		return m_fileHead.channel;
	}
	uint64_t get_lba_start()
	{
		if(m_pFile == NULL)
		{
			return -1;
		}
		return m_pFile->get_lba_start();
	}
	bool is_closed()
	{
		if(NULL == m_pFile)
		{
			return  true;
		}
		return m_pFile->is_closed();
	}

	bool is_append()
	{
		if(m_fileHead.writeStatus == STFS_REC_WT_APPEND)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	int GetCacheWater()
	{
		if(m_pfifo_buf == NULL)
		{
			return 0;
		}
		return m_pfifo_buf->GetCurWarter();
	}
protected:
	STFS_REC_FILEHEAD		m_fileHead;			//文件信息头
	CSerialFileBase *       m_pFile;	    //线性文件
	char            *       m_storageName;  // 存储设备名
	CFIFO_buf*              m_pfifo_buf;		//FIFO Buf  用于缓冲录像，对齐录像数据到扇区的整数倍的
	uint32_t				m_curPos;			//数据当前位置（文件）
	CFrmUtility             m_frmTool;          //帧头检测工具
	CRecFrameIdx            m_frmIdx;			//索引
	uint32_t                m_idxDiffTm;        //索引间隔时间
    STFS_REC_IDX			m_recIdx;			//单条件索引记录
	STFS_GPS_IDX            m_gpsIdx;			//GPS信息索引记录
    STFS_REC_SEG			m_curSeg;			//当前录像段
    vector<STFS_REC_SEG>	m_vecSeg;			//录像段容器
    int64_t					m_endTime;			//录像结束时间
	uint32_t                m_lastFrmNO;		//上一帧的编号
	uchar_t *				m_headBuf;			//数据缓冲
	uint32_t                m_headBufSize;		//头信息大小
	uint32_t                m_idxDiffComSize;  //I帧索引间隔的通常的长度
	uint32_t                m_fileHeadOffset;  //文件头信息偏移
	bool	   m_bstop;					//是否停止录像
	bool	   m_bsuspense;				// 暂停
	int        m_channel;				//通道
	bool       m_bRawDev;              //是否为裸磁盘设备Linux为 /dev/sda 
	bool       m_bUseFIFO;			  //是否使用缓存
	int        m_switchMod;			 //文件切换模式
};

#ifdef _WIN32
const int	FIFO_SIZE = SIZE_MB(3);
#else
const int	FIFO_SIZE = SIZE_MB(2);
#endif

#endif //_REC_WRITE_BASE_
